# Purchasing from a kiosk

One of the base functionalities of the SDK is a seamless purchasing flow, allowing for ease of rules
resolving (hiding away the calls). The SDK supports all four rules by default, and works for
`TESTNET` and `MAINNET`. To support other networks,
[follow the instructions in the Introduction](../introduction#using-kioskclient-on-devnet-or-localnet).

## How to purchase

By default, the SDK places the item in the caller's kiosk, unless there's a lock rule, in which case
it locks it.

Them following is an example of a purchase call.

```typescript
const item = {
	itemType: '0x..::hero::Hero',
	itemId: '0x..',
	price: 100000n,
	sellerKiosk: '0xSellerKiosk',
};

// Assume `kioskClient` and `cap` are supplied to the function as explained in the previous section.
const tx = new Transaction();
const kioskTx = new KioskTransaction({ transaction: tx, kioskClient, cap });

await kioskTx.purchaseAndResolve({
	itemType: item.itemType,
	itemId: item.itemId,
	price: item.price,
	sellerKiosk: item.sellerKiosk,
});

kioskTx.finalize();

// Sign and execute transaction.
await signAndExecuteTransaction({ tx: tx });
```

> The function queries for a TransferPolicy for that item, and if a policy is found, it
> automatically resolves all the rules, one by one. You can add a custom rule resolver in the
> `KioskClient` instance, with instructions on how to resolve a custom rule. Read more in the next
> section.

## Supporting a custom rule

You can use the `purchaseAndResolve` function to support a custom rule.

```typescript
const kioskClient = new KioskClient({...});
const myCustomRule = {
		rule: `0xMyRuleAddress::game_rule::Rule`,
		packageId: `0xMyRuleAddress`,
        // The resolving function. This is called when calling the `purchaseAndResolve`.
		resolveRuleFunction: (params: RuleResolvingParams) => {
            // By knowing the params we have here, we can extract the variables we need to resolve this rule.
            const { transaction, itemType, packageId, extraArgs } = params;
            const { gamePass } = extraArgs;
            if(!gamePass) throw new Error("GamePass not supplied");

            // Calls the game's rule prove function, which could, for example
            // allow rules to resolve only if the holder has a gamePass object.
            transaction.moveCall({
                target: `${packageId}::game_rule::prove_pass`,
                typeArguments: [itemType],
                arguments: [transferRequest, transaction.object(gamePass)],
            });
        },
};
// This allows rules resolution from the `purchaseAndResolve` function.
kioskClient.addRuleResolver(myCustomRule);

// Assume `cap` is supplied to the function as explained in the introduction section.
const tx = new Transaction();
const kioskTx = new KioskTransaction({ transaction: tx, kioskClient, cap });

await kioskTx.purchaseAndResolve({
	itemType: item.itemType,
	itemId: item.itemId,
	price: item.price,
	sellerKiosk: item.sellerKiosk,
	extraArgs: {
		gamePass: '0xMyGamePassObjectId'
	}
});

kioskTx.finalize();

// Sign and execute transaction.
await signAndExecuteTransaction({ tx: tx });
```

```typescript
// For reference, here's the RuleResolvingParams contents.
type RuleResolvingParams = {
	transaction: Transaction;
	itemType: string;
	itemId: string;
	price: string;
	policyId: ObjectArgument;
	kiosk: ObjectArgument;
	ownedKiosk: ObjectArgument;
	ownedKioskCap: ObjectArgument;
	transferRequest: TransactionArgument;
	purchasedItem: TransactionArgument;
	packageId: string;
	extraArgs: Record<string, any>; // extraParams contains more possible {key, values} to pass for custom rules.
};
```
